#!/usr/bin/python
# coding=utf8
###############################################################################
#                                                                             #
#    Copyright 2007 Pavel Nemec, Danil Dotsenko                               #
#                                                                             #
#    This program is free software; you can redistribute it and/or modify     #
#    it under the terms of the GNU General Public License as published by     #
#    the Free Software Foundation; either version 2 of the License, or        #
#    (at your option) any later version.                                      #
#                                                                             #
#    This program is distributed in the hope that it will be useful,          #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
#    GNU General Public License for more details.                             #
#                                                                             #
#    You should have received a copy of the GNU General Public License        #
#    along with this program; if not, write to the Free Software              #
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA #
#                                                                             #
###############################################################################                                   #
# contacts: Pavel Nemec <pnemec@suse.cz>
#           Danil Dotsenko, dd@accentsolution.com

#from __future__ import division
import karambino
import re
import os
import time
import readvcard

###########################################################################
# applet settings go here, and are accessible from everywhere as global
class configClass:
	'''
	An all-encoupasing, application-wide configuration storage location.
	This will be shared even between concurrently running themes.
	This is the equivalent of root directory of Windows registry.
	'''
	def __init__(self):
		self.common={
			'name':'KabSA',
			'version':"June, 2007",
			'author':"Team",
			'cleanStart':False,
			'interval':10000,
			}
		self.canvas = None # nothing fancy. Just a variable to which the whole set of objects will be rooted.
#
##########################################################

def setupObjects(widget):
	global appConfig
	widget_w, widget_h = 200, 300
	karambino.resizeWidget(widget, widget_w, widget_h)
	# these pertain to the tileList widget. Budget extra space for decor.
	# our frame's size is (20, 15, 20, 25) (l, t, r, b)
	padd_l,padd_t,padd_r, padd_b = 2, 2, 2, 2
	padd_vert = padd_t+padd_b
	padd_hor = padd_l+padd_r
	tile_h, tile_n = 54, 6 # height of tile, # of tiles

	########################
	bg = karambino.imageClass(path='images/m_center.png', widget=widget)
	bg.size(widget_w-padd_hor,40)
	bg.position(padd_l, padd_t) # offset from 0,0 by the thickness of frame
	theme = 't'
	framepieces = [
	'images/'+theme+'_aa.png',
	'images/'+theme+'_ab.png',
	'images/'+theme+'_ac.png',
	'images/'+theme+'_ba.png',
	'images/'+theme+'_bc.png',
	'images/'+theme+'_ca.png',
	'images/'+theme+'_cb.png',
	'images/'+theme+'_cc.png',
	]
	karambino.addFrame(bg,framepieces)
	
	karambino.redrawstatus = False
	
	#header.button = karambino.imageClass('images/aqua_selected.png', parent=header)
	#header.button.move(-10,0, "e","e")
	#header.button.busy = time.time()+0.5 # we may be using threads. This will help identify work-in-progress.

	# this is the input field
	bg.inputbox = karambino.inputClass('', w=widget_w-20, h=30, parent=bg)
	bg.inputbox.move(0,0,'center','center') # 0, 0 is offset from coordinates center obj, center parent

	# this is the function that is triggered when text is entered into
	# search field. We search, assign the new results object to list
	# and refresh the list.
	def processsearch(*args, **kw):
		# print "search req here, args are %s\n kw are %s" % (args, kw)
		# inputbox's submit() (Enter button pressed) signal returns key "text" in kw with contents of inputbox at time of Enter press.
		data = appConfig.sch(bg.inputbox.text())
		if len(data) == 1:
			bg.listbox.data = []
			populatedetail(data[0], bg.details)
		else:
			if bg.details.visible:
				bg.details.hide(redraw=False)
			bg.listbox.data = data
		bg.listbox.refresh()
	bg.inputbox.clicked.signalset('keypress',processsearch)

	#bg.logo = karambino.imageClass(karambino.kdehelper.iconPath('kaddressbook', 32),parent=bg)
	#bg.logo.move(4,0,'w','w')

	bg.listbox = karambino.tileListClass(parent=bg)	
	#karambino.redrawstatus = True # end of wrapping the actions in blanket no-draw.
	bg.listboxshow = False # will be a flag. Means the ball, not the drop is shown.

	karambino.kdehelper.screeninfo()

	def resizeApplet():
		# NOT USED YET
		'''
		This hand-calculates the dimensions of known things and resizes the applet
		using essenrially karamba.resizeWidget(widget, x,y)
		'''
		# we inherit all of the variables from the parent function.
		if h >= dl: # there is less or equal number of data than set up tiles.
			if header.toolbar.visible:
				header.toolbar.hide()
			newH = 70+dl*th*int(header.listboxshow) # header height + data length * tile height
		else: # there are more data pieces. Thus, we also show the toolbar
			if not header.toolbar.visible:
				header.toolbar.show()
			newH = 70+h*th*int(header.listboxshow)
		karambino.resizeWidget(widget, w, newH)
		listbox.size(w, newH-70)
	# end of Header code.
	#########################

	############################################
	# setting up the Tile List
	# listbox = karambino.tileListClass(parent=header) # moved to the top, to make available

	def buildtile(parent, tilenum, *args, **kw):
		# you can use globals to get your settings from your main scope into here.
		# the following variables of parent may also help you:
		# parent.width = width of the tileList container.
		# parent.visibletiles
		# parent.tileheight
		#
		# this is going to be tile's background and "arent" for all other elements.
		tile = karambino.textClass('', w=parent.width, h=parent.tileheight, parent=parent)
		#tile.logo = karambino.imageClass(path=karambino.kdehelper.iconPath('unknown', 48), parent=tile)
		#tile.logo.move(-8,0,'e','e',redraw=False) # move 4 pixels from NorthWest corner of parent.
		tile.back = karambino.imageClass('images/m_center.png', w=parent.width-4, h=parent.tileheight-4, parent=tile)
		tile.back.move(2,2,'nw','nw')
		tile.back.mousein.signalset('tilemenu', tileMenu, obj=tile, direction='in')
		tile.back.mouseout.signalset('tilemenu', tileMenu, obj=tile, direction='out')
		karambino.addFrame(tile.back, framepieces)
		tile.title = karambino.textClass('', w=widget_w-padd_hor*2, h=20, parent=tile)
		tile.title.text.color(0,0,0, redraw=False)
		tile.title.text.align("LEFT", redraw=False)
		tile.title.move(0,3, 'n', 'n', redraw=False)
		tile.title.text.size(11, redraw=False)
		tile.title.text.font("DejaVu Sans Mono", redraw=False)
		tile.title['maxchars']= 1000 # bypassing this problem at this time.
		# int((widget_w-padd_hor*2)/karambino.kdehelper.fontpxwidth(12)) # storing this just for ease of access and use in populate fn.
		tile.info = karambino.textClass('', w=widget_w-padd_hor*2-4, h=20, parent=tile)
		tile.info.text.color(70,70,70, redraw=False)
		tile.info.text.align("LEFT", redraw=False)
		tile.info.text.size(10, redraw=False)
		tile.info.move(2,2,'n','s', tile.title, redraw=False)
		return tile
	bg.listbox.buildtile = buildtile
	
	def populatetile(tile, dataunit, *args, **kw):
		'''
		tile - pointer to tile object.
		dataunit - pointer to data object from which to collect the pieces and populate the tile.
		'''
		nick = dataunit('NICKNAME')
		if nick:
			name = dataunit.name()+' ('+nick+')'
		else:
			name = dataunit.name()
		#if len(name) > tile.title['maxchars']:
			#name = name[:tile.title['maxchars']-1]+'.'
		tile.title.text(name,redraw=False)
		info = dataunit('TEL')
		if not info:
			info = dataunit('EMAIL')
		if not info:
			info = dataunit.formatAddress(template='$CITY, $REGION')
		tile.info.text(info,redraw=False)
		#tile.logo.path(dataunit['icon'],redraw=False)
		# you don't need to redraw for the whole function because right after it,
		# we move the tile and redraw anyway.
	bg.listbox.populatetile = populatetile

	# FLOATING MENU
	bg.listbox.menu = karambino.textClass('', w=88, h=1, parent=bg.listbox)
	menu = bg.listbox.menu
	menu.position(0, -140) # menu's parent - listview - will be moved down. Hence, we put thisway up high
	menu.dataptr = None # will be the pointer to the tile above which we hover.
	menu.hidden=True #this is just a flag for our show / hide routine

	def tileMenu(*args, **kw):
		'''
		Shows, rePopulates and Hides the menu that hovers above each tile.
		We are activated by MouseIn/Out, so:
		kw['x'],kw['y'],kw['button'],kw['obj'],kw['direction']
		obj is the tile main obj. direction is "in" or "out"
		'''
		if kw['direction']=='in': #  mouse if over a tile now, draw the menu.
			menu.move(0, 0, "center","center",kw['obj'], redraw=False)
			menu.dataptr = menu.parent.data[kw['obj'].datanum] # pointer to the data
			#menu.clippint.tip('Internal link: '+menu.parent.data[kw['obj'].datanum]['intpath'])
			menu.hidden = False
		elif not menu.hidden: # kw['direction']=='out' is already assumed
			# hiding the menu.
			menu.position(0, -40)
			menu.hidden = True

	# finishing listbox init.
	bg.listbox.initialize(bg.listbox, data=appConfig.sch[''], w=widget_w, h=4, tileheight=tile_h)
	bg.listbox.move(-padd_l,padd_b, 'nw', 'sw')

	# hiding the hover menu when scrolling the list.
	bg.listbox.scrollup.signalset('tilemenu', tileMenu, obj=None, direction='out')
	bg.listbox.scrolldown.signalset('tilemenu', tileMenu, obj=None, direction='out')
	# hiding the hover menu when text is typed in the search field
	bg.inputbox.clicked.signalset('tilemenu', tileMenu, obj=None, direction='out')

	def menuItemClicked(*args, **kw):
		if kw['action']=='showmore':
			#karambino.execute('dcop klipper klipper setClipboardContents "'+kw['obj'].data['extpath']+'"')
			bg.listbox.data = []
			bg.listbox.refresh()
			bg.inputbox.text(bg.inputbox.text()+' ')
			populatedetail(kw['obj'].dataptr, bg.details)
		elif kw['action']=='clippint':
			karambino.execute('dcop klipper klipper setClipboardContents "'+kw['obj'].data['intpath']+'"')

	karambino.redrawstatus = False # saving cycles :)
	
	menu.back = karambino.imageClass('images/m_center.png', w=48, h=44, parent=menu)
	menu.back.move(0,0,'center','center')
	karambino.addFrame(menu.back,framepieces)
	menu.btn1 = karambino.imageClass(karambino.kdehelper.iconPath('viewmag', 32), parent=menu)
	menu.btn1.move(0, 0, 'center','center')
	menu.btn1.clicked.btn[1].signalset('click', menuItemClicked, action='showmore', obj=menu)
	#menu.btn1.logo = karambino.imageClass(karambino.kdehelper.iconPath('knetworkmanager', 16), parent=listbox.menu.clippext)
	#menu.btn1.logo.move(0, 5, 'sw','sw')
	#menu.btn2 = karambino.imageClass(karambino.kdehelper.iconPath('editcopy', 32), parent=menu)
	#menu.btn2.move(13+35, 0, 'w','w')
	#menu.btn2.clicked.btn[1].signalset('click', menuItemClicked, action='clippint', obj=listbox.menu)
	#menu.btn2.logo = karambino.imageClass(karambino.kdehelper.iconPath('network', 16), parent=listbox.menu.clippint)
	#menu.btn2.logo.move(0, 5, 'sw','sw')
	# End of FLoating menu

	# DETAILS WIDGET - the one displaying all of the contact's info.
	# things we will show:
	# full name 'N'
	# nickname or title
	# Sets of Home / Work of following pieces
	#  EMAIL, TEL, FAX, ADDRESS
	# At this time, it will be only one flat NON-SCROLLABLE area
	# the eventual goal is to move the Home/Work sections to scrollable tiles.
	#
	# anyway... let's begin...
	# note, we the parent for this object will be bg.listbox, not bg. This is done for ease of positioning.
	details = karambino.imageClass('images/m_center.png', w=widget_w-padd_hor, h=widget_h-30,parent=bg)
	karambino.addFrame(details, framepieces)

	details.title = karambino.textClass('title', w=widget_w-padd_hor-6,h=20,parent=details)
	details.info = karambino.textClass('info', w=widget_w-padd_hor-6,h=20,parent=details)
	details.menu = karambino.textClass('', w=widget_w-padd_hor-6,h=1,parent=details)
	details.phone = karambino.textClass('phone', w=widget_w-padd_hor-6,h=20,parent=details)
	details.email = karambino.textClass('email', w=widget_w-padd_hor-6,h=20,parent=details)
	details.address = karambino.textClass('address', w=widget_w-padd_hor-6,h=20,parent=details)
	details.move(0,6,'n','s')
	details.title.move(0,3,'n','n')
	details.info.move(0,3,'n','s', details.title)
	details.menu.move(0,3,'n','s', details.info)
	details.phone.move(0,3,'n','s', details.menu)
	details.email.move(0,26,'n','s', details.menu)
	details.address.move(0,46,'n','s', details.menu)
	details.title.text.size(12)
	details.info.text.size(12)
	details.menu.text.size(10)
	details.phone.text.size(12)
	details.email.text.size(10)
	details.address.text.size(10)
	bg.details = details
	bg.details.hide()
	
	karambino.redrawstatus = True

	#resizeApplet()
	#
	##########################################

def populatedetail(data, obj):
	# full name 'N'
	# nickname or title
	# Sets of Home / Work of following pieces
	#  EMAIL, TEL, FAX, ADDRESS
	# At this time, it will be only one flat NON-SCROLLABLE area
	# the eventual goal is to move the Home/Work sections to scrollable tiles
	# Available objects: title, info, [menu], phone, email, address
	
	# title is a 'N', which is different from FN (formatted name)
	# we default to FN in the case of the main list, but here we can show all.
	karambino.redrawstatus = False
	if not obj.visible:
		obj.show()
	obj.title.text(data.name(['N','FN','ORG']))
	tmp = data('NICKNAME')
	if tmp:
		tmp = '('+tmp+') '
	tmp = tmp + data('TITLE')
	obj.info.text(tmp)
	obj.phone.text(data('TEL'))
	obj.email.text(data('EMAIL'))
	karambino.redrawstatus = True
	obj.address.text(data.formatAddress())

##################################################################
# Testing & helper functions.
#
def notificationWindow(message, title="", mode=None):
	'''
	This uses KDE kdialog app and asks for main body message and window title.
	App name is attached automatically
	'''
	global appConfig
	myCommand = 'kdialog --caption "'+appConfig.common['name']+'" --title "'+title+'" --msgbox "'+message+'"'
	karambino.execute(myCommand)
#
##################################################################

##################################################################
# KARAMBA's built-in functions
#
#this is called when you widget is initialized
def initWidget(widget):
	global appConfig
	appConfig=configClass()
	#print appConfig.helper.getIconPath(appConfig.helper.getIconForMime(mimetypes.guess_type('file:///home/dd/tile.txt')[0]))
	appConfig.common['cleanStart'] = True
	# web server set up.
	# unpackParts(widget)
	import uservcards
	appConfig.sch = readvcard.cachedSearch(uservcards.getUserFiles())
	setupObjects(widget)
	# if not success:
	#    burn this boat.
	karambino.redrawWidget(widget)

def widgetUpdated(widget):
	global appConfig
	if appConfig.common['cleanStart']:
		karambino.changeInterval(widget, appConfig.common['interval'])

def widgetClosed(widget):
	global appConfig
	try:
		a = karambino.getcfg('threadfarm')
		a.stop()
	except:
		pass

# NOTE you must use acceptDrops() to make this work
# but if you are using Karambino, it will do it for you if you set a signal to obj.itemDropped()
def itemDropped(widget, dropText, x=0, y=0):
	karambino.itemDropped(widget, dropText, x, y)

#  botton = button clicked:
#                    1 = Left Mouse Button
#                    2 = Middle Mouse Button
#                    3 = Right Mouse Button
#                    4,5 = Scroll wheel up and down
def widgetClicked(widget, x, y, button):
	karambino.widgetClicked(widget, x, y, button)

# NOTE you must use attachClickArea() to make a meterClicked work
# but if you are using Karambino, it will do it for you if you set a signal to obj.clicked()
def meterClicked(widget, meter, button):
	karambino.meterClicked(widget, meter, button)

def keyPressed(widget, meter, char):
	karambino.keyPressed(widget, meter, char)

def themeNotify(widget, caller, message):
	pass
	
def widgetMouseMoved(widget, x, y, button):
	#Warning:  Don't do anything too intensive here
	karambino.widgetMouseMoved(widget, x, y, button)

# This will be printed when the widget loads.
print "Horovod is loaded."